/*
 * Copyright 2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.paboo.chart.pdf417;

import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import org.paboo.util.*;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

/**
 * @author Leonard
 */
@RestController
@ExposesResourceFor(Pdf417Entity.class)
public class Pdf417Controller {

    private static final long serialVersionUID = 2978803208721169289L;
    private LoggerFactoryUtils log = LoggerFactoryUtils.getInstance().load(Pdf417Controller.class);


    @RequestMapping("/pdf417")
    public ResponseEntity<byte[]> handleControllerExecution(HttpMethod method,
                                                            @RequestParam(value = "data", required = false, defaultValue = ParameterObject.DEFAULT_CONTECT) String ctx,
                                                            @RequestParam(value = "encoding", required = false, defaultValue = ParameterObject.DEFAULT_ENCODING) String encoding,
                                                            @RequestParam(value = "eclevel", required = false, defaultValue = "2") int ecLevel,
                                                            @RequestParam(value = "format", required = false, defaultValue = "png") String format,
                                                            @RequestParam(value = "size", required = false, defaultValue = "300x100") String size,
                                                            @RequestParam(value = "margin", required = false, defaultValue = "5") int margin,
                                                            @RequestBody(required = false) String requestStr) throws IOException {
        Pdf417Entity e = new Pdf417Entity();
        MediaType mt;

        if (!ParameterObject.hasText(requestStr)) {
            requestStr = "{}";
        }

        if (method == HttpMethod.GET) {

            ctx = URLDecoder.decode(ctx, encoding);
            log.debug(">>>" + ctx + " - " + encoding);
            e.setData(ctx);
            e.setEncoding(encoding);

            if (!(ecLevel >= 0 && ecLevel <= 8) || !ZxingValidation.checkFormat(format.toLowerCase()) || "text".equalsIgnoreCase(format)) {
                String errorMsg = "Data parameter ERROR!";
                log.warn(errorMsg);
                return ResponseUtils.sendErrorOverJSON(HttpStatus.BAD_REQUEST, errorMsg);
            }

            e.setErrorCorrectionLevel(ecLevel);
            e.setFormat(format.toLowerCase());

            if (size.contains("x")) {
                String[] sizea = size.split("x");
                if (ZxingValidation.isNum(sizea[0]) && ZxingValidation.isNum(sizea[1])) {
                    e.setWidth(Integer.parseInt(sizea[0]));
                    e.setHeight(Integer.parseInt(sizea[1]));
                }
            }

            e.setMargin(margin);

            log.info(new Gson().toJson(e));
        } else if (method == HttpMethod.POST) {
            //BufferedReader br = new BufferedReader(new InputStreamReader(requestIS, encoding));
            Gson gson = new Gson();
            try {
                e = gson.fromJson(requestStr, Pdf417Entity.class);
            } catch (JsonIOException | JsonSyntaxException ex) {
                String errorMsg = "DATA IS NOT JSON!!!";
                log.error(errorMsg);
                return ResponseUtils.sendErrorOverJSON(HttpStatus.BAD_REQUEST, errorMsg);
            }

            if (e == null) {
                e = new Pdf417Entity();
            }
            if (!ParameterObject.hasText(e.getData())) {
                e.setData(ParameterObject.DEFAULT_CONTECT);
            }
            if (!ParameterObject.hasText(e.getEncoding()) || "null".equalsIgnoreCase(e.getEncoding())) {
                e.setEncoding(StandardCharsets.ISO_8859_1.name());
            }
            if (e.getErrorCorrectionLevel() == null) {
                e.setErrorCorrectionLevel(2);
            }
            if (!ParameterObject.hasInteger(e.getMargin())) {
                e.setMargin(30);
            }
            if (!ParameterObject.hasInteger(e.getWidth()) || !ParameterObject.hasInteger(e.getHeight())) {
                e.setWidth(300);
                e.setHeight(100);
            }
            if (!ParameterObject.hasText(e.getFormat()) ||
                    !ZxingValidation.checkFormat(e.getFormat().toLowerCase()) ||
                    "text".equalsIgnoreCase(e.getFormat())) {
                e.setFormat("png");
            }
            log.info(gson.toJson(e));
        } else {
            return ResponseUtils.sendErrorOverJSON(HttpStatus.NOT_IMPLEMENTED, "Request error!!!");
        }

        BitMatrix matrix;
        try {
            matrix = ZxingUtil.encode(e);
            log.debug("Matrix size:" + matrix.getWidth() + "x" + matrix.getHeight());
        } catch (WriterException ex) {
            String errorMsg = "Create Fail!!!";
            if (log.isDebugEnabled()) {
                log.error(errorMsg, ex);
            } else {
                log.error(errorMsg);
            }

            return ResponseUtils.sendErrorOverJSON(HttpStatus.INTERNAL_SERVER_ERROR, errorMsg);
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        if (!ImageIO.write(ZxingImage.toBufferedImage(matrix, ZxingImage.WHITE, ZxingImage.BLACK), e.getFormat().toLowerCase(), out)) {
            String errmsg = "Could not write an image of format " + e.getFormat().toLowerCase();
            log.error(errmsg);
            return ResponseUtils.sendErrorOverJSON(HttpStatus.INTERNAL_SERVER_ERROR, errmsg);
        }

        if (e.getFormat().equalsIgnoreCase("png")) {
            mt = MediaType.IMAGE_PNG;
        } else if (e.getFormat().equalsIgnoreCase("gif")) {
            mt = MediaType.IMAGE_GIF;
        } else if (e.getFormat().equalsIgnoreCase("jpg") || e.getFormat().equalsIgnoreCase("jpeg")) {
            mt = MediaType.IMAGE_JPEG;
        } else {
            return ResponseUtils.sendErrorOverJSON(HttpStatus.NOT_IMPLEMENTED, "Not implement image");
        }

        return ResponseUtils.sendOkContent(mt, out.toByteArray());
    }
}
